/* 
 * Author: vdaras
 */


#include "Font.h"
#include "Color.h"
#include "SDLException.h"
#include "Surface.h"
#include "TextImage.h"

namespace sdl
{

/*
 * Constructor opens a font on disk. If it fails an exception is thrown indicating
 * the error.
 *
 * @param
 * - path: font's location on disk
 * - size: desired size
 */

Font::Font(const std::string &path, int size)
{
    m_font = NULL;

    //load primitive font, if loading fails
    if(!Open(path, size))
    {
        //indicate failure
        throw Exception(TTF_GetError());
    }

    //get info for the opened font
    m_fontAscent = TTF_FontAscent(m_font);
    m_fontDescent = TTF_FontDescent(m_font);
    m_fontHeight = TTF_FontHeight(m_font);
    m_fontLineSkip = TTF_FontLineSkip(m_font);

    //calculate average advance using the ascii table
    int minX, maxX, minY, maxY ,advance;
    int totalAdvance = 0;
    int cnt = 0;
    for(Uint16 c = 0x01; c <= 0x7F; ++c)
    {
        TTF_GlyphMetrics(m_font, c, &minX, &maxX, &minY, &maxY, &advance);
        totalAdvance += advance;
        ++cnt;
    }

    m_averageAdvance = totalAdvance / cnt;
}


Font::~Font()
{
    if(m_font)
    {
        TTF_CloseFont(m_font);
    }
}


/*
 * Returns the ascent value of the font.
 */

int Font::GetAscent()
{
    return m_fontAscent;
}


/*
 * Returns the descent value of the font.
 */

int Font::GetDescent()
{
    return m_fontDescent;
}


/*
 * Returns the height of the font.
 */

int Font::GetHeight()
{
    return m_fontHeight;
}


/*
 * Returns the proposed line skip for the font.
 */

int Font::GetLineSkip()
{
    return m_fontLineSkip;
}


/*
 * Returns the average character advance of this font.
 */

int Font::GetAverageAdvance()
{
    return m_averageAdvance;
}


/*
 * This routine returns the size of the given string in pixels.
 *
 * @param
 * - text: the string
 * - width: OUTPUT paremeter, the width of the string in pixels
 * - height: OUTPUT paremeter, the height of the string in pixels
 */

void Font::TextSize(const std::string& text, int& width, int& height)
{
    TTF_SizeText(m_font, text.c_str(), &width, &height);
}


/*
 * This rtoune returns the size of a glyph(character) in pixels.
 *
 * @param
 * - glyph: the glyph
 * - width: OUTPUT parameter, the width of the glyph in pixels
 * - height: OUTPUT paremeter, the height of the glyph in pixels
 */

void Font::GlyphSize(Uint16 glyph, int &minX, int &maxX, int &minY, int &maxY, int &advance)
{
    TTF_GlyphMetrics(m_font, glyph, &minX, &maxX, &minY, &maxY, &advance);
}


/*
 * This routine renders text on the desired surface @ x,y.
 *
 * - x, y: text's upper-left coordiantes
 * - text: text to be rendered
 * - color: text's color
 * - target: target surface
 */

bool Font::RenderText(int x, int y, const std::string& text, const Color& color, Surface* target) const
{
    //render text on a primitive surface
    SDL_Color primitiveColor = { color.GetR(), color.GetG(), color.GetB() };
    SDL_Surface *txt = TTF_RenderText_Solid(m_font, text.c_str(), primitiveColor);

    //if rendering was successfull
    if(txt)
    {
        //blit primitive surface
        SDL_Rect rect;
        rect.x = x;
        rect.y = y;
       
        SDL_BlitSurface(txt, NULL, target->GetBuffer(), &rect);

        //free text memory
        SDL_FreeSurface(txt);

        //indicate success
        return true;
    }

    //indicate failure
    return false;
}


/*
 * Opens a font on disk. If it fails the method returns failure and the caller
 * should handle it.
 *
 * @param
 * - path: font's location on disk
 * - size: desired size
 */

bool Font::Open(const std::string& path, int size)
{
    //load a temporary primitive font from disk
    m_font = TTF_OpenFont(path.c_str(), size);

    //if loading was successful indicate success
    if(m_font)
        return true;

    //indicate failure
    return false;
}


/*
 * Indicates if the primitive font is not equal to NULL
 */

bool Font::Valid() const
{
    return m_font != NULL;
}


/*
 * This method creates and sdl::TextImage and returns it to the caller. This is
 * the only way to create an sdl::TextImage.
 */

std::auto_ptr< sdl::TextImage > Font::GetImage(const std::string& text, const Color& color) const
{
    //create text
    SDL_Color primitiveColor = { color.GetR(), color.GetG(), color.GetB() };
    SDL_Surface *m_buffer = TTF_RenderText_Solid(m_font, text.c_str(), primitiveColor);

    //create an image return
    sdl::TextImage *toReturn = new sdl::TextImage;

    //set the image's buffer as the text
    toReturn->SetBuffer(m_buffer);

    //return the image
    return std::auto_ptr< sdl::TextImage >(toReturn);
}

};
